package dvdstore.client.connector;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Set;

import common.dvd.DVD;
import common.rmi.DVDStoreServerInterface;

import dvdstore.client.exception.ServerConnectionException;

public class ServerConnector implements DVDStoreServerInterface{
	
	private Registry registry;
	
	private DVDStoreServerInterface dvdStoreServerStub;
	
	private int numberOfRetries;
	
	/* 
	 * The class ServerConnector has no static attributes, so its initialization 
	 * is trivial. As a result, the inner class ServerConnectorHolder will not be 
	 * initialized when ServerConnector is initialized. The inner class will only 
	 * be initialized when the getInstance() method is called. Since the class 
	 * initialization is serial (non-concurrent) we guarantee that the execution of 
	 * the constructor of ServerConnector() is also serial and thus thread-safe.
	 */
	private static class ServerConnectorHolder {
		public static ServerConnector instance = new ServerConnector();
	}
	
	private ServerConnector() {
		numberOfRetries = 0;
		connectToServer();
	}
	
	private void connectToServer() {
		try {
			registry = LocateRegistry.getRegistry("localhost");
			dvdStoreServerStub = (DVDStoreServerInterface) registry.lookup("DVDStoreServer");
			numberOfRetries = 0;
		} catch (Exception ex) {
			if(numberOfRetries >= 5) {
				throw new ServerConnectionException("Could not connect to server after 5 retries. Error: " + ex.getMessage());
			} else {
				System.out.println("Server is down, retrying in 5 seconds.");
				numberOfRetries ++;
				try {
				    Thread.sleep(5000);
				} catch(InterruptedException ex2) {
					/* The thread can be interrupted while sleeping. This exception
					 * must be treated.
					 */
				    Thread.currentThread().interrupt();
				}
				connectToServer();
			}
		} 
	}
	
	public static ServerConnector getInstance() {
		return ServerConnectorHolder.instance;
	}
	
	@Override
	public Set<DVD> getAllDVDs()  {
		try {
			return dvdStoreServerStub.getAllDVDs();
		}catch (RemoteException ex) {
			try {
				connectToServer();
				return getAllDVDs();
			} catch (ServerConnectionException ex2) {
				throw ex2;
			}
		}
	}
	
	@Override
	public DVD getDVDByID(Long id)  {
		try {
			return dvdStoreServerStub.getDVDByID(id);
		}catch (RemoteException ex) {
			try {
				connectToServer();
				return getDVDByID(id);
			} catch (ServerConnectionException ex2) {
				throw ex2;
			}
		}
	}
}